package main

import (
	"bytes"
	"crypto/sha256"
	"encoding/binary"
	"log"
	"time"
)

/**
区块链结构
	1.版本号
	2.前区块hash
	3.MerKle 根 梅克尔根
	4.时间戳
	5.难度目标
	6.Nonce
 *
*/
// 1.定义区块链结构
type Block struct {
	Version    uint64 //版本号
	PrevHash   []byte //前区块哈希
	MerKleRoot []byte //merkle 根
	Timestamp  uint64 //时间戳
	Difficulty uint64 //难度值(调整⽐特币挖矿的难度)
	Nonce      uint64 //随机数，这就是挖矿时所要寻找的数
	Hash       []byte //当前区块哈希
	Data       []byte //数据

}

// 2.创建一个区块 (需要数据和前区块hash)
func NewBlock(data string, prvBlockHash []byte) *Block {
	block := Block{
		Version:    00,
		PrevHash:   prvBlockHash,
		MerKleRoot: []byte{}, //先填为空，v4版本再详解
		Timestamp:  uint64(time.Now().Unix()),
		Difficulty: 100,
		Nonce:      100,
		//Hash: []byte{}, //先填为空，后⾯再进⾏计算
		Data: []byte(data),
	}
	//设置hash
	//block.SetHash()
	pow := NewProofOfWork(block)
	hash, nonce := pow.Run()
	block.Hash = hash
	block.Nonce = nonce

	return &block
}

// 实现一个辅助函数，把uint 转换成byte
// Uint64ToByte 将 uint64 转换为 []byte
func Uint64ToByte(num uint64) []byte {
	var buffer bytes.Buffer
	//将数据以⼆进制形式保存到buffer中
	err := binary.Write(&buffer, binary.BigEndian, num)
	if err != nil {
		log.Panic(err)
	}
	return buffer.Bytes()
}

// 3.生成hash
func (b *Block) SetHash() {
	var blockByteInfo []byte //存储拼接好的数据，最后作为sha256函数的参数
	//1. 拼接当前区块的数据
	blockByteInfo = append(blockByteInfo, b.PrevHash...)
	blockByteInfo = append(blockByteInfo, b.Data...)
	blockByteInfo = append(blockByteInfo, b.MerKleRoot...)
	blockByteInfo = append(blockByteInfo, Uint64ToByte(b.Version)...)
	blockByteInfo = append(blockByteInfo, Uint64ToByte(b.Timestamp)...)
	blockByteInfo = append(blockByteInfo, Uint64ToByte(b.Difficulty)...)
	blockByteInfo = append(blockByteInfo, Uint64ToByte(b.Nonce)...)
	/*
		tem :=[][]byte{
			Uint64ToByte(b.Version),
			Uint64ToByte(b.Timestamp),
			Uint64ToByte(b.Difficulty),
			Uint64ToByte(b.Nonce),
			b.PrevHash,
			b.Data,
			b.MerKleRoot,
		}*/
	// 2. 计算哈希值，使用SHA256算法对拼接后的数据进行哈希计算
	hash := sha256.Sum256(blockByteInfo)
	// 3. 将计算得到的哈希值赋值给当前区块的Hash字段 ( hash[:] 可以将这个固定长度的数组转换为 []byte 类型)
	b.Hash = hash[:]
}
